﻿using System.Diagnostics;
using Castle.DynamicProxy;
using Framework.Core.Reflection;

namespace Framework.Core.Cache
{
    public class CacheInterceptor : IInterceptor, IFlushable
    {
        private readonly ICacheKeyBuilder cacheKeyBuilder;
        private readonly IMethodFactory methodFactory;
        private readonly IStorage storage;

        public CacheInterceptor(ICacheKeyBuilder cacheKeyBuilder, IStorage storage)
            : this(cacheKeyBuilder, storage, new MethodFactory())
        {
        }

        internal CacheInterceptor(ICacheKeyBuilder cacheKeyBuilder, IStorage storage, IMethodFactory methodFactory)
        {
            this.cacheKeyBuilder = cacheKeyBuilder;
            this.storage = storage;
            this.methodFactory = methodFactory;
        }

        public void Flush()
        {
            storage.Flush();
        }

        public void Intercept(IInvocation invocation)
        {
            var key = cacheKeyBuilder.Build(methodFactory.Create(invocation));
            object cache;

            if (storage.TryGet(key, out cache))
            {
                invocation.ReturnValue = cache;
            }
            else
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                invocation.Proceed();
                var result = invocation.ReturnValue;
                storage.Update(key, stopwatch.Elapsed, result);
            }
        }
    }
}